//
// Created by Ivor_ on 2023/2/28.
//

#include "specialFunctions.h"

double legendreSF(int order, double variable) {
    if (order == 0) {
        return 1.;
    } else if (order == 1) {
        return variable;
    }
    double temp1, temp2 = 1., temp3 = variable;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = ((2.*i + 1.)*variable*temp2 - 1.*i*temp1) / (1.+i);
    }
    return temp3;
}

double* legendresSF(int order, double variable) {
    double* legendres = (double*)calloc(order+1llu, sizeof(double));
    legendres[0] = 1.;
    if (order == 0) {
        return legendres;
    }
    legendres[1] = variable;
    if (order == 1) {
        return legendres;
    }
    for (int i = 1; i < order; ++i) {
        legendres[i+1] = ((2.*i + 1.)*variable* legendres[i] - 1.*i* legendres[i-1]) / (1.+i);
    }
    return legendres;
}

double legendreDerivativeSF(int order, double variable) {
    if (order == 0) {
        return 0.;
    } else if (order == 1) {
        return 1.;
    }
    double temp1, temp2 = 1., temp3 = variable, temp4;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = ((2.*i + 1.)*variable*temp2 - 1.*i*temp1) / (1.+i);
        temp4 = (-1.*i*variable*temp2 + 1.*i*temp1) / (1. - variable*variable);
    }
    return temp4;
}

double* legendreDerivativesSF(int order, double variable) {
    double* legendreDs = (double*)calloc(order+1llu, sizeof(double));
    legendreDs[0] = 0.;
    if (order == 0) {
        return legendreDs;
    }
    legendreDs[1] = 1.;
    if (order == 1) {
        return legendreDs;
    }
    double temp1, temp2 = 1., temp3 = variable;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = ((2.*i + 1.)*variable*temp2 - 1.*i*temp1) / (1.+i);
        legendreDs[i+1] = (-1.*i*variable*temp2 + 1.*i*temp1) / (1. - variable*variable);
    }
    return legendreDs;
}

double laguerreSF(int order, double variable) {
    if (order == 0) {
        return 1.;
    } else if (order == 1) {
        return 1. - variable;
    }
    double temp1, temp2 = 1., temp3 = 1. - variable;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = ((2.*i + 1. - variable)*temp2 - 1.*i*temp1) / (1.+i);
    }
    return temp3;
}

double* laguerresSF(int order, double variable) {
    double* laguerres = (double*)calloc(order + 1llu, sizeof(double));
    laguerres[0] = 1.;
    if (order == 0) {
        return laguerres;
    }
    laguerres[1] = 1. - variable;
    if (order == 1) {
        return laguerres;
    }
    for (int i = 1; i < order; ++i) {
        laguerres[i+1] = ((2.*i + 1. - variable)*laguerres[i] - 1.*i*laguerres[i-1]) / (1.+i);
    }
    return laguerres;
}

double laguerreDerivativeSF(int order, double variable) {
    if (order == 0) {
        return 0.;
    } else if (order == 1) {
        return -1.;
    }
    double temp1, temp2 = 1., temp3 = 1. - variable, temp4;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = ((2.*i + 1. - variable)*temp2 - 1.*i*temp1) / (1.+i);
        temp4 = (1.*i*temp2 - 1.*i*temp1) / variable;
    }
    return temp4;
}

double* laguerreDerivativesSF(int order, double variable) {
    double* laguerreDs = (double*)calloc(order+1llu, sizeof(double));
    laguerreDs[0] = 0.;
    if (order == 0) {
        return laguerreDs;
    }
    laguerreDs[1] = -1.;
    if (order == 1) {
        return laguerreDs;
    }
    double temp1, temp2 = 1., temp3 = variable;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = ((2.*i + 1. - variable)*temp2 - 1.*i*temp1) / (1.+i);
        laguerreDs[i+1] = (1.*i*temp2 - 1.*i*temp1) / variable;
    }
    return laguerreDs;
}

double hermiteSF(int order, double variable) {
    if (order == 0) {
        return 1.;
    } else if (order == 1) {
        return 2.*variable;
    }
    double temp1, temp2 = 1., temp3 = 2.*variable;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = 2.*variable*temp2 - 2.*i*temp1;
    }
    return temp3;
}

double* hermitesSF(int order, double variable) {
    double* hermites = (double*)calloc(order + 1llu, sizeof(double));
    hermites[0] = 1.;
    if (order == 0) {
        return hermites;
    }
    hermites[1] = 2.*variable;
    if (order == 1) {
        return hermites;
    }
    for (int i = 1; i < order; ++i) {
        hermites[i+1] = 2.*variable*hermites[i] - 2.*i*hermites[i-1];
    }
    return hermites;
}

double hermiteDerivativeSF(int order, double variable) {
    if (order == 0) {
        return 0.;
    } else if (order == 1) {
        return 2.;
    }
    double temp1, temp2 = 1., temp3 = 1. - variable, temp4;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = 2.*variable*temp2 - 2.*i*temp1;
        temp4 = 2.*i*temp2;
    }
    return temp4;
}

double* hermiteDerivativesSF(int order, double variable) {
    double* hermiteDs = (double*)calloc(order+1llu, sizeof(double));
    hermiteDs[0] = 0.;
    if (order == 0) {
        return hermiteDs;
    }
    hermiteDs[1] = 2.;
    if (order == 1) {
        return hermiteDs;
    }
    double temp1, temp2 = 1., temp3 = 1. - variable;
    for (int i = 1; i < order; ++i) {
        temp1 = temp2;
        temp2 = temp3;
        temp3 = 2.*variable*temp2 - 2.*i*temp1;
        hermiteDs[i+1] = 2.*i*temp2;
    }
    return hermiteDs;
}

struct specialFunctionsCallCategory sf(const char* functionName) {
    struct specialFunctionsCallCategory sfcc;
    if (!strcmp(functionName, "Legendre")) {
        sfcc.function = legendreSF;
        sfcc.functions = legendresSF;
        sfcc.derivative = legendreDerivativeSF;
        sfcc.derivatives = legendreDerivativesSF;
        return sfcc;
    } else if (!strcmp(functionName, "Laguerre")) {
        sfcc.function = laguerreSF;
        sfcc.functions = laguerresSF;
        sfcc.derivative = laguerreDerivativeSF;
        sfcc.derivatives = laguerreDerivativesSF;
        return sfcc;
    } else if (!strcmp(functionName, "Hermite")) {
        sfcc.function = hermiteSF;
        sfcc.functions = hermitesSF;
        sfcc.derivative = hermiteDerivativeSF;
        sfcc.derivatives = hermiteDerivativesSF;
        return sfcc;
    } else {
        fprintf(stderr, "Invalid function %s, only Legendre, Laguerre and Hermite are included!\n", functionName);
        exit(-1);
    }
}
